package cn.jhz.learn.blog.service.core.auth.article.impl;

import static java.util.stream.Collectors.toList;

import cn.jhz.learn.blog.common.util.ApplicationContextHelper;
import cn.jhz.learn.blog.component.GLOBAL;
import cn.jhz.learn.blog.component.ValidationHolder;
import cn.jhz.learn.blog.dao.article.BlogArticleAccessMapper;
import cn.jhz.learn.blog.dao.article.BlogArticleContentMapper;
import cn.jhz.learn.blog.dao.article.BlogArticleMapper;
import cn.jhz.learn.blog.dao.article.BlogArticleSubfieldAssociativeMapper;
import cn.jhz.learn.blog.po.article.BlogArticle;
import cn.jhz.learn.blog.po.article.BlogArticleContent;
import cn.jhz.learn.blog.po.article.BlogArticleSubfieldAssociative;
import cn.jhz.learn.blog.service.core.auth.article.ConsoleArticleService;
import cn.jhz.learn.blog.vo.article.query.view.ArticleView;
import cn.jhz.learn.blog.vo.article.query.page.ConsolePageView;
import cn.jhz.learn.blog.vo.article.submit.ArticleParam;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service("consoleArticleService")
public class ConsoleArticleServiceImpl implements ConsoleArticleService {

    private final BlogArticleMapper articleMapper;
    private final BlogArticleAccessMapper accessMapper;
    private final BlogArticleSubfieldAssociativeMapper associativeMapper;
    private final BlogArticleContentMapper contentMapper;

    @Autowired
    public ConsoleArticleServiceImpl(BlogArticleMapper articleMapper, BlogArticleAccessMapper accessMapper, BlogArticleSubfieldAssociativeMapper associativeMapper, BlogArticleContentMapper contentMapper) {
        this.articleMapper = articleMapper;
        this.accessMapper = accessMapper;
        this.associativeMapper = associativeMapper;
        this.contentMapper = contentMapper;
    }

    @Override
    public Integer create(Integer uid, @NotNull ArticleParam param) {
        BlogArticle article = BlogArticle.builder().build();
        article.setBlogId(ValidationHolder.getCurrentUser().getId());
        article.setSticky(GLOBAL.RESOURCE.STICK.IS_NOT);
        article.setReleaseTime(new Date());
        BlogArticleContent content = BlogArticleContent.builder()
                .content(param.getContent())
                .license(param.getConfirm().orElse(null))
                .resourceLink(param.getLink().orElse(null))
                .build();
        BeanUtils.copyProperties(param, article);

        /*自调用:导致不改变配置的情况下, 不使用属性注入, 不使用新类的解决办法*/
        Objects.requireNonNull(
                ApplicationContextHelper.popBean(
                        "consoleArticleService", this.getClass()))
                .insertArticle(article, content, param.getSubfield());

        return article.getId();
    }

    @Transactional
    @Override
    public void delete(Integer uid, Integer id, Boolean deep) {
        BlogArticle article = (BlogArticle) ValidationHolder.getParam(GLOBAL.VALIDATION_KEY.CLASS_BLOG_ARTICLE.ID + id);
        article.setStatus((deep ?  GLOBAL.RESOURCE.STATUS.DELETE : GLOBAL.RESOURCE.STATUS.DISABLE));
        articleMapper.updateByPrimaryKeySelective(article);
        associativeMapper.batchUpdateStatusByArticleId((deep ? GLOBAL.RESOURCE.STATUS.DELETE : GLOBAL.RESOURCE.STATUS.DISABLE), id);
    }

    @Override
    public void update(Integer uid, @NotNull ArticleParam param) {
        BlogArticle article =
                (BlogArticle) ValidationHolder.getParam(GLOBAL.VALIDATION_KEY.CLASS_BLOG_ARTICLE.ID + param.getId().get());
        BlogArticleContent content = BlogArticleContent.builder()
                .id(param.getId().get())
                .content(param.getContent())
                .license(param.getConfirm().orElse(null))
                .resourceLink(param.getLink().orElse(null))
                .build();
        BeanUtils.copyProperties(param, article);

        List<BlogArticleSubfieldAssociative> associative =
                associativeMapper.batchSelectByArticleId(param.getId().get());
        List<BlogArticleSubfieldAssociative> newAssociative = Lists.newArrayList();

        if(param.getSubfield().isPresent())
            newAssociative.addAll(param.getSubfield().get().stream()
                    .filter(e -> {
                        for(BlogArticleSubfieldAssociative temp : associative){
                            if(temp.getSubfieldId().equals(e)){
                                associative.remove(temp);
                                return false;
                            }else
                                temp.setStatus(GLOBAL.RESOURCE.STATUS.DELETE);
                        }
                        return true; })
                    .map(e ->
                            BlogArticleSubfieldAssociative.builder()
                                    .status(GLOBAL.RESOURCE.STATUS.VALID)
                                    .releaseTime(new Date())
                                    .articleId(param.getId().get())
                                    .subfieldId(e)
                                    .build()
                    ).collect(toList()));


        Objects.requireNonNull(
                ApplicationContextHelper.popBean("consoleArticleService", this.getClass()))
                        .updateArticle(article, content, associative.stream()
                                        .map(BlogArticleSubfieldAssociative::getId)
                                        .collect(toList()), newAssociative
                        );
    }

    @Transactional
    @Override
    public void stick(Integer uid, Integer id, Boolean sticky) {
        BlogArticle article = (BlogArticle) ValidationHolder.getParam(GLOBAL.VALIDATION_KEY.CLASS_BLOG_ARTICLE.ID + id);
        article.setSticky((sticky ? GLOBAL.RESOURCE.STICK.IS: GLOBAL.RESOURCE.STICK.IS_NOT));
        articleMapper.updateByPrimaryKey(article);
    }

    @Transactional
    @Override
    public void recycle(Integer uid, Integer id) {
        BlogArticle article = (BlogArticle) ValidationHolder.getParam(GLOBAL.VALIDATION_KEY.CLASS_BLOG_ARTICLE.ID + id);
        article.setStatus(GLOBAL.RESOURCE.STATUS.DRAFT);
        articleMapper.updateByPrimaryKeySelective(article);
    }

    @Override
    public Map<String, Object> getDetail(Integer uid, Integer id) {
        return null;
    }

    @Override
    public Map<String, Object> getCountAndList(Integer uid, @NotNull ConsolePageView pageView) {
        PageHelper.startPage(pageView.getPage_num(), pageView.getPage_size());
        Map<String, Object> result = Maps.newHashMap();

        List<Byte> status, visibility, type;

        if(pageView.getVisibility().isPresent()){
            switch (pageView.getVisibility().get()){
                case 1:
                    visibility = Lists.newArrayList(GLOBAL.RESOURCE.VISIBILITY.OPEN);
                    break;
                case 0:
                case 2:
                    visibility = Lists.newArrayList(
                            GLOBAL.RESOURCE.VISIBILITY.SUBSCRIBE,
                            GLOBAL.RESOURCE.VISIBILITY.PRIVATE
                    );
                    break;
                default:
                    throw new RuntimeException("visibility参数异常"+pageView.getVisibility().get());
            }
            status = Lists.newArrayList(GLOBAL.RESOURCE.STATUS.VALID);
        }else if(pageView.getStatus().isPresent()){
            switch (pageView.getStatus().get()){
                case 2:
                    status = Lists.newArrayList(GLOBAL.RESOURCE.STATUS.DRAFT);
                    break;
                case 3:
                    status = Lists.newArrayList(GLOBAL.RESOURCE.STATUS.AUDIT);
                    break;
                case 4:
                    status = Lists.newArrayList(GLOBAL.RESOURCE.STATUS.DISABLE);
                    break;
                default:
                    throw new RuntimeException("status"+pageView.getStatus().get());
            }
            visibility =
                    ValidationHolder.getLevel(GLOBAL.VALIDATION_KEY.OWN_ENTITY_BLOG.ARTICLES.VISIBILITY + ValidationHolder.getCurrentUser().getId());
        }else{
            status =
                    ValidationHolder.getLevel(GLOBAL.VALIDATION_KEY.OWN_ENTITY_BLOG.ARTICLES.STATUS + ValidationHolder.getCurrentUser().getId());
            visibility =
                    ValidationHolder.getLevel(GLOBAL.VALIDATION_KEY.OWN_ENTITY_BLOG.ARTICLES.VISIBILITY + ValidationHolder.getCurrentUser().getId());
        }
        type = Lists.newArrayList(GLOBAL.RESOURCE.OWN_LEVEL.ALL);

        List<BlogArticle> temps =
                articleMapper.selectLatestByBlogIdOrSubfieldId(
                        ValidationHolder.getCurrentUser().getId(),
                        null,
                        type,
                        visibility,
                        status
                );


        PageInfo tempInfo = PageInfo.of(temps);

        tempInfo.setList(temps.stream().map(info->{
            ArticleView temp = new ArticleView();
            temp.setAccess(accessMapper.countByArticleId(info.getId()));
            temp.setReplyCount(accessMapper.countByArticleId(info.getId()));
            BeanUtils.copyProperties(info, temp);
            return temp;
        }).collect(toList()));

        result.put("page_Info", tempInfo);
        result.put("count", articleMapper.countByBlogId(ValidationHolder.getCurrentUser().getId()));

        return result;
    }

    @Transactional
    public void insertArticle(@NotNull BlogArticle article,
                              @NotNull BlogArticleContent content,
                              @NotNull Optional<List<Integer>> subfieldIds){
        articleMapper.insert(article);
        content.setId(article.getId());
        contentMapper.insertSelective(content);

        List<BlogArticleSubfieldAssociative> associative = Lists.newArrayList();
        subfieldIds.ifPresent(
                integers -> associative.addAll(
                        integers.stream()
                                .map(
                                        id -> BlogArticleSubfieldAssociative
                                                .builder()
                                                .releaseTime(article.getReleaseTime())
                                                .articleId(article.getId())
                                                .subfieldId(id)
                                                .status(GLOBAL.RESOURCE.STATUS.VALID).build()
                                )
                                .collect(toList())));
        if(!associative.isEmpty())
            associativeMapper.batchInsert(associative);
    }

    @Transactional
    public void updateArticle(BlogArticle article,
                              BlogArticleContent content,
                              @NotNull List<Integer> toUpdateList,
                              List<BlogArticleSubfieldAssociative> toInsertList){
        articleMapper.updateByPrimaryKey(article);
        contentMapper.updateByPrimaryKeySelective(content);
        if(!toUpdateList.isEmpty())
            associativeMapper.batchUpdateStatusByPrimaryKey((byte) 0, toUpdateList);
        if(!toInsertList.isEmpty())
            associativeMapper.batchInsert(toInsertList);
    }
}
